package org.hydrakyoufeng.stuplatform.utils;

import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.hydrakyoufeng.stuplatform.config.AliyunConfig;
import org.hydrakyoufeng.stuplatform.exception.ProcessException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.Random;
import java.util.concurrent.TimeUnit;

/**
 * 阿里云工具类，将发送验证码和验证验证码都放在了这里
 */
@Component
@Slf4j
public class MsgCodeUtils {

    @Autowired
    private AliyunConfig.AliyunProperties aliyunProperties;

    private Config config = null;

    /**
     * 验证码种子
     */
    private String codeSeed = "0123456789";

    private Random random = new Random();

    @Autowired
    private RedisTemplate redisTemplate;

    @PostConstruct
    public void doInit() {
        config = new Config();
        config.setAccessKeyId(aliyunProperties.getAccessKeyId());
        config.setAccessKeySecret(aliyunProperties.getAccessKeySecret());
        config.endpoint = "dysmsapi.aliyuncs.com";
        log.info("阿里云短信配置初始化完成");
    }

    /**
     * 创建阿里云短信客户端
     * @return
     */
    private Client createClient() throws Exception {
        return new Client(config);
    }

    /**
     * 向指定手机号码发送验证码短信。
     * 因为只有一个模板，所以模板和模板参数都写死在了程序里面。
     * @param mobile
     */
    public void sendMsg(String mobile,String code) throws Exception {

        SendSmsRequest sendSmsRequest = new SendSmsRequest()
                .setSignName("阿里云短信测试")
                // 固定的测试模板
                .setTemplateCode("SMS_154950909")
                .setPhoneNumbers(mobile)
                .setTemplateParam("{\"code\":\"" + code + "\"}");
        RuntimeOptions runtime = new RuntimeOptions();
        try {
            Client client = createClient();
            client.sendSmsWithOptions(sendSmsRequest, runtime);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ProcessException("发送短信失败");
        }
    }

    /**
     * 获取6位数的验证码
     *
     * @return
     */
    private String getCode() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            sb.append(codeSeed.charAt(random.nextInt(codeSeed.length())));
        }
        return sb.toString();
    }


    /**
     * 对外可以提供现成逻辑的方法，为了测试方便，将随机生成的验证码进行返回
     * 获取验证码，并发送（调用阿里云短信接口），发送成功后将验证码存储到redis中。
     * @return
     */
    public String getAndSendCode(String mobile) throws Exception {
        String code = getCode();
        // 下面是发送短信的逻辑，开发中可以将下面代码注释掉，避免产生短信发送费用
//        sendMsg(mobile,code);
        // 保存在redis中，key使用手机号码做前缀，value使用验证码的值，验证码有效时间：10分钟。
        redisTemplate.opsForValue().set(mobile + "_msg",code, 10, TimeUnit.MINUTES);
        // 返回，以方便前端测试
        return code;
    }


    /**
     * 校验手机验证码是否正确
     * @param mobile
     * @param code
     * @throws Exception
     */
    public void validateCode(String mobile,String code) throws Exception {

        // 保存在redis中，key使用手机号码做前缀，value使用验证码的值，验证码有效时间：10分钟。
        String codeFromRedis = (String) redisTemplate.opsForValue().get(mobile + "_msg");
        // 判断验证码是否过期
        if (codeFromRedis == null) {
            throw new ProcessException("验证码已经失效");
        }
        // 无论成功与否，都删除验证码，避免使用同样的验证码，导致绕过验证码
        // 可以自行进行扩展，让验证码允许错误3次，3次之后，验证码失效
        redisTemplate.delete(mobile + "_msg");
        // 判断验证码是否正确
        if (codeFromRedis.equals(code)) {
            return;
        }
        throw new ProcessException("验证码错误");
    }
}
